/*
 * Powered By XY
 * Since 2020
 */

package com.xy.biz.fd.service.impl;

import com.xy.biz.base.client.DictClient;
import com.xy.biz.base.domain.dto.DictDTO;
import com.xy.biz.base.domain.req.GetDictRequest;
import com.xy.biz.fd.domain.bo.DocumentBO;
import com.xy.biz.fd.domain.bo.DocumentCodePoolBO;
import com.xy.biz.fd.domain.bo.DocumentCodeRuleBO;
import com.xy.biz.fd.domain.converter.DocumentCodeRuleConverter;
import com.xy.biz.fd.domain.converter.DocumentConverter;
import com.xy.biz.fd.domain.query.DocumentCodePoolQuery;
import com.xy.biz.fd.domain.query.DocumentQuery;
import com.xy.biz.fd.domain.req.SaveDocumentCodePoolRequest;
import com.xy.biz.fd.domain.req.documentCode.GetDocumentByCodeRequest;
import com.xy.biz.fd.domain.req.documentCode.GetDocumentCodeRuleRequest;
import com.xy.biz.fd.domain.resp.GetDocumentCodeRuleResponse1;
import com.xy.biz.fd.domain.resp.GetDocumentResponse;
import com.xy.biz.fd.manager.DocumentCodePoolManager;
import com.xy.biz.fd.manager.DocumentCodeRuleManager;
import com.xy.biz.fd.manager.DocumentManager;
import com.xy.biz.fd.strategy.DocumentCodeGenerator;
import com.xy.biz.fd.service.DocumentCodeRuleService;
import com.xy.biz.uc.client.UserClient;
import com.xy.biz.uc.domain.dto.UserDTO;
import com.xy.biz.uc.domain.req.GetUserRequest;
import com.xy.lang.domain.LongIdCodeName;
import com.xy.lang.domain.Result;
import com.xy.lang.exception.ObjectNotFoundException;
import com.xy.lang.util.ResultUtils;
import com.xy.tool.concurrent.locks.DisLock;
import com.xy.tool.concurrent.locks.DisLockFactory;
import com.xy.tool.context.UserContextHolder;
import com.xy.tool.util.BeanUtilsEx;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.xy.biz.fd.strategy.DocumentCodeGenerator.PREFIX_DOCUMENT_CODE_GENERATOR;

/**
 *
 *
 * @author zsp
 * @date 2021-4-29
 */
@Service
public class DocumentCodeRuleServiceImpl implements DocumentCodeRuleService {

    @Autowired
    private DocumentCodeRuleManager documentCodeRuleManager;
    @Autowired
    private DocumentManager documentManager;
    @Autowired
    private DisLockFactory disLockFactory;
    @Autowired
    private DictClient dictClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private DocumentCodePoolManager documentCodePoolManager;
    @Autowired
    private Map<String, DocumentCodeGenerator> generatorMap;

    private final static String LOCK_PREFIX = "document_code_rule_";
    private static volatile boolean flag = false;

    @Override
    public String getCode(GetDocumentCodeRuleRequest request) {
        DocumentCodeRuleBO documentCodeRuleBO = getDocumentCodeRule(request);
        if (documentCodeRuleBO == null) {
            return null;
        }
        flag = false;
        return getDocumentCodeGenerator(documentCodeRuleBO.getType()).generate(documentCodeRuleBO);
    }

    @Override
    public String generateCode(GetDocumentCodeRuleRequest request) {
        DocumentCodeRuleBO oldDocumentCodeRuleBO = getDocumentCodeRule(request);
        LocalDateTime now = LocalDateTime.now();
        if (oldDocumentCodeRuleBO == null) {
            return null;
        }
        List<DocumentCodePoolBO> documentCodePoolBOS = documentCodePoolManager.listDocumentCodePool(DocumentCodePoolQuery.max().name(oldDocumentCodeRuleBO.getName()).reuseFlag(true).build());
        if (!CollectionUtils.isEmpty(documentCodePoolBOS)) {
            //先从编号池中取最早的编号
            documentCodePoolBOS.sort(Comparator.comparing(DocumentCodePoolBO::getUpdateTime));
            DocumentCodePoolBO documentCodePoolBO = documentCodePoolBOS.get(0);
            DocumentCodePoolBO req = DocumentCodePoolBO.builder()
                    .documentCode(documentCodePoolBO.getDocumentCode())
                    .reuseFlag(false)
                    .updateTime(now).build();
            documentCodePoolManager.updateDocumentCodePoolByBO(req);
            return documentCodePoolBO.getDocumentCode();
        }
        if (flag) {
            flag = false;
            String code = getDocumentCodeGenerator(oldDocumentCodeRuleBO.getType()).generate(oldDocumentCodeRuleBO);
            //存入编号池
            documentCodePoolManager.saveDocumentCodePool(DocumentCodePoolBO.builder()
                    .documentCodeRuleId(oldDocumentCodeRuleBO.getDocumentCodeRuleId())
                    .documentCode(code)
                    .name(oldDocumentCodeRuleBO.getName())
                    .tenantId(0L)
                    .createTime(now)
                    .reuseFlag(false)
                    .updateTime(now).build());
            return code;
        }
        DocumentCodeRuleBO newDocumentCodeRuleBO = BeanUtilsEx.copyProperties(oldDocumentCodeRuleBO, () -> new DocumentCodeRuleBO());
        newDocumentCodeRuleBO.increment();
        newDocumentCodeRuleBO.setUpdateTime(LocalDateTime.now());
        int affectedRows = documentCodeRuleManager.updateDocumentCodeRule(oldDocumentCodeRuleBO, newDocumentCodeRuleBO);
        if (affectedRows > 0) {
            String code = getDocumentCodeGenerator(newDocumentCodeRuleBO.getType()).generate(newDocumentCodeRuleBO);
            //存入编号池
            documentCodePoolManager.saveDocumentCodePool(DocumentCodePoolBO.builder()
                    .documentCodeRuleId(newDocumentCodeRuleBO.getDocumentCodeRuleId())
                    .documentCode(code)
                    .name(newDocumentCodeRuleBO.getName())
                    .tenantId(0L)
                    .createTime(now)
                    .reuseFlag(false)
                    .updateTime(now).build());
            return code;
        }
        String name = generateName(request);
        if (name.contains("null")) {
            return null;
        }
        String lockResource = LOCK_PREFIX + name;
        DisLock disLock = disLockFactory.create();
        if (disLock.lock(lockResource)) {
            try {
                oldDocumentCodeRuleBO = documentCodeRuleManager.getDocumentCodeRule(name);
                newDocumentCodeRuleBO = BeanUtilsEx.copyProperties(oldDocumentCodeRuleBO, () -> new DocumentCodeRuleBO());
                newDocumentCodeRuleBO.increment();
                newDocumentCodeRuleBO.setUpdateTime(LocalDateTime.now());
                documentCodeRuleManager.updateDocumentCodeRule(oldDocumentCodeRuleBO, newDocumentCodeRuleBO);
                String code = getDocumentCodeGenerator(newDocumentCodeRuleBO.getType()).generate(newDocumentCodeRuleBO);
                //存入编号池
                documentCodePoolManager.saveDocumentCodePool(DocumentCodePoolBO.builder()
                        .documentCodeRuleId(newDocumentCodeRuleBO.getDocumentCodeRuleId())
                        .documentCode(code)
                        .name(newDocumentCodeRuleBO.getName())
                        .tenantId(0L)
                        .createTime(now)
                        .reuseFlag(false)
                        .updateTime(now).build());
                return code;
            } finally {
                disLock.unlock(lockResource);
            }
        }

        return null;
    }

    /**
     * 获取文档编号信息
     * // TODO 文档编号格式改变需要改此处
     * @param request
     * @return
     */
    @Override
    public Result<GetDocumentCodeRuleResponse1> getCodeInfo(GetDocumentCodeRuleRequest request) {
        String code = request.getCode();
        // TODO
       /* if (!DocumentCodeFormatValidationUtils.validation(code)) {
            return Result.fail();
        }*/
        code = code.substring(0, code.lastIndexOf('-'));
        DocumentCodeRuleBO documentCodeRule = documentCodeRuleManager.getDocumentCodeRule(code);
        return Result.success(DocumentCodeRuleConverter.toResponse1(documentCodeRule));
    }


    public static void main(String[] args) {
        String str = "HM-EW-#@-KK";
        String substring = str.substring(0, str.lastIndexOf('-'));
        System.out.println(substring);
    }
    /**
     * 通过文件编号获取文件信息
     */
    @Override
    public Result<GetDocumentResponse> getDocumentByCode(GetDocumentByCodeRequest request) {
        /*if (!DocumentCodeFormatValidationUtils.validation(request.getCode())) {
            return Result.fail("", "文件编号格式不正确！");
        }*/
        //判断当前用户是否是QA
//        String substring = request.getCode().substring(0, request.getCode().lastIndexOf('-'));
//        DocumentCodeRuleBO documentCodeRule = documentCodeRuleManager.getDocumentCodeRule(substring);
        DocumentBO documentBO = documentManager.getLatestVersion(DocumentQuery.one().documentCode(request.getCode()).latestVersionFlag(0).build());
        UserDTO user = ResultUtils.get(userClient.getUser(GetUserRequest.builder().userId(UserContextHolder.getUserId()).withRole(true).build()));
        if (Objects.isNull(user) || CollectionUtils.isEmpty(user.getRoleList())) {
            return Result.fail("", "用户无角色！");
        }
//        Set<String> roleNameSet = user.getRoleList().stream().map(LongIdCodeName::getName).collect(Collectors.toSet());
//        if (!roleNameSet.contains("QA") && documentCodeRule.getType() == 1) {
//            //return Result.fail("", "用户非QA角色，不能处理质量手册！");
//        }
        if (documentBO == null) {
            return Result.fail("", "无此编号文件！");
        }
        return Result.success(DocumentConverter.toResponse(documentBO));
    }

    @Override
    public Result<Boolean> getDocumentCodeIsExisted(GetDocumentByCodeRequest request) {
        List<DocumentBO> documentBOList = documentManager.listDocument(DocumentQuery.max()
                .documentCode(request.getCode())
                .documentFlowLock(false)
                .deleteFlag(false)
                .build());
        return Result.success(CollectionUtils.isEmpty(documentBOList));
    }

    private DocumentCodeRuleBO getDocumentCodeRule(GetDocumentCodeRuleRequest request) {
        String name = generateName(request);
        if (name.contains("null")) {
            return null;
        }
        DocumentCodeRuleBO documentCodeRuleBO = documentCodeRuleManager.getDocumentCodeRule(name);
        //有此key则取数据，若无，则上锁新建key
        if (null == documentCodeRuleBO) {
            String lockResource = LOCK_PREFIX + name;
            DisLock disLock = disLockFactory.create();
            if (disLock.lock(lockResource)) {
                try {
                    documentCodeRuleBO = documentCodeRuleManager.getDocumentCodeRule(name);
                    if (null == documentCodeRuleBO) {
                        documentCodeRuleBO = documentCodeRuleManager.saveDocumentCodeRule(DocumentCodeRuleBO.builder()
                                .name(name)
                                .type(request.getType())
                                .serialNumber(1)
                                .showLength(request.getShowLength())
                                .tenantId(UserContextHolder.getTenantId())
                                .createTime(LocalDateTime.now())
                                .updateTime(LocalDateTime.now())
                                .build());
                        flag = true;
                    }
                } finally {
                    disLock.unlock(lockResource);
                }
            }
        }

        return documentCodeRuleBO;
    }

    private DocumentCodeGenerator getDocumentCodeGenerator(Integer type) {
        DocumentCodeGenerator documentCodeGenerator = generatorMap.get(PREFIX_DOCUMENT_CODE_GENERATOR + type);
        if (documentCodeGenerator == null) {
            throw new ObjectNotFoundException();
        }
        return documentCodeGenerator;
    }

    /**
     * @author Greekn Zhang
     * @date 2021/5/13 10:34
     * @return java.lang.String
     * @description 生成文件规则名称
     */
    private String generateName(GetDocumentCodeRuleRequest request) {
        // TODO 此处写死
        DictDTO dictDTO = ResultUtils.get(dictClient.getDict(GetDictRequest.builder().dictId(60L).build()));
        StringBuffer sb = new StringBuffer(dictDTO.getDictCode() + "-");
        sb.append(UserContextHolder.getUserGroupAbbr()).append("-").append(request.getCode());
        return sb.toString();
    }
}
